<?php if (!defined('BASEPATH'))
    exit('No direct script access allowed');


/*
* @author ThangTB
* calculator data
* */
class CalculatorData
{

    public function getDataOutPut($aData1, $aData2, $symbol1, $symbol2)
    {
        $aArrayClose1 = $this->getArrayClosePrice($aData1);
        $aArrayClose2 = $this->getArrayClosePrice($aData2);
        $aArrayDate = $this->getArrayDate($aData1);
        $aArrayRatio = $this->getArrayRatio($aArrayClose1, $aArrayClose2);
        $aArraySTDEV = $this->getArraySTDEV($aArrayRatio);
        $aArrayAverage = $this->getArrayAverage($aArrayRatio);
        $aArraySDMean = $this->sdMean($aArrayRatio, $aArraySTDEV, $aArrayAverage);
        $aArraySignal = $this->getArraySignal($aArraySDMean, $symbol1, $symbol2);
        $aArraySpread = $this->spread($aArrayClose1, $aArrayClose2);

        $aData = array();
        $count_aArrayDate = count($aArrayDate);
        for ($i = 0; $i < $count_aArrayDate; $i++) {
            $aData[$i][0] = $aArrayDate[$i];
            if (isset($aArrayClose1[$i])) {
                $aData[$i][1] = $aArrayClose1[$i];
            } else {
                $aData[$i][1] = "";
            }
            if (isset($aArrayClose2[$i])) {
                $aData[$i][2] = $aArrayClose2[$i];
            } else {
                $aData[$i][2] = "";
            }

            // $aData[$i][2] = $aArrayClose2[$i];
            $aData[$i][3] = number_format($aArrayRatio[$i], 5);
            if (isset($aArraySTDEV[$i])) {
                // $aData[$i][4] = $aArraySTDEV[$i];
                $aData[$i][4] = number_format($aArraySTDEV[$i], 5);
            } else {
                $aData[$i][4] = "";
            }
            if (isset($aArrayAverage[$i])) {
                // $aData[$i][5] = $aArrayAverage[$i];
                $aData[$i][5] = number_format($aArrayAverage[$i], 5);
            } else {
                $aData[$i][5] = "";
            }
            if (isset($aArraySDMean[$i])) {
                // $aData[$i][6] = $aArraySDMean[$i];
                $aData[$i][6] = number_format($aArraySDMean[$i], 5);
            } else {
                $aData[$i][6] = "";
            }

            if (isset($aArraySignal[$i])) {
                $aData[$i][7] = $aArraySignal[$i];
            } else {
                $aData[$i][7] = "";
            }

            $aData[$i][8] = $aArraySpread[$i];
            $aData[$i][8] = round($aArraySpread[$i], 3);
            // $aData[$i][8] = number_format($aArraySpread[$i], 5);
        }
        return $aData;
    }

    public function getArrayDate($aData)
    {
        $aDate = array();
        $count_aData = count($aData);
        for ($i = 0; $i < $count_aData; $i++) {
            $oEntity = $aData[$i];
            $aDate[$i] = $oEntity->getDate();
        }
        return $aDate;
    }

    public function getArrayClosePrice($aData)
    {
        $aClosePrice = array();
        $count_aData = count($aData);
        for ($i = 0; $i < $count_aData; $i++) {
            $oEntity = $aData[$i];
            $aClosePrice[$i] = $oEntity->getClosePrice();
            // $aClosePrice[$i] = $oEntity['close_price'];
        }
        return $aClosePrice;
    }

    /*
    * get array of ratio
    * */
    public function getArrayRatio($aClosePrice1, $aClosePrice2)
    {
        $aRatio = array();
        $count_aClosePrice1 = count($aClosePrice1);
        for ($i = 0; $i < $count_aClosePrice1; $i++) {
            if (!isset($aClosePrice2[$i]) || floatval($aClosePrice2[$i]) == 0) {
                $aRatio[$i] = 0;
            } else {
                $aRatio[$i] = floatval($aClosePrice1[$i]) / floatval($aClosePrice2[$i]);
            }
        }
        return $aRatio;
    }

    public function getArraySTDEV($aRatio)
    {
        $aArraySTDEV = array();
        $count_aRatio = count($aRatio);
        for ($i = 0; $i < $count_aRatio; $i++) {
            $aCal = array();
            $h = 0;
            for ($j = $i; $j < ($i + 100); $j++) {
                if ($i + 100 > $count_aRatio) {
                    break;
                }
                $aCal[$h] = $aRatio[$j];
                $h++;
            }
            $count_aCal = count($aCal);
            if ($count_aCal < 100) {
                break;
            } else {
                $aArraySTDEV[$i] = $this->standard_deviation($aCal);
                $h = 0;
            }
        }

        return $aArraySTDEV;
    }

    /*
    * get array of STDEV
    * @param $aValues ratio array
    * */
    function standard_deviation($aValues, $bSample = false)
    {
        $fMean = array_sum($aValues) / count($aValues);
        $fVariance = 0.0;
        foreach ($aValues as $i) {
            $fVariance += pow($i - $fMean, 2);
        }
        $fVariance /= ($bSample ? count($aValues) - 1 : count($aValues));
        return (float)sqrt($fVariance);
    }

    public function getArrayAverage($aRatio)
    {
        $aArrayAverage = array();
        $count_aRatio = count($aRatio);
        for ($i = 0; $i < $count_aRatio; $i++) {
            $aCal = array();
            $h = 0;
            for ($j = $i; $j < ($i + 100); $j++) {
                if ($i + 100 > $count_aRatio) {
                    break;
                }
                $aCal[$h] = $aRatio[$j];
                $h++;
            }

            if (count($aCal) < 100) {
                break;
            } else {
                $aArrayAverage[$i] = $this->average($aCal);
                $h = 0;
            }
        }

        return $aArrayAverage;
    }
    /*
    * get average of array
    * */
    public function average($aValues)
    {
        $sum = 0;
        $count_aValues = count($aValues);
        for ($i = 0; $i < $count_aValues; $i++) {
            $sum += floatval($aValues[$i]);
        }
        return floatval($sum / count($aValues));
    }

    /*
    * get sdmean
    * */
    public function sdMean($aRatio, $aSTDEV, $aAverage)
    {
        $aSDMean = array();
        $count_aSTDEV = count($aSTDEV);
        for ($i = 0; $i < $count_aSTDEV; $i++) {
            if (!isset($aSTDEV[$i]) || floatval($aSTDEV[$i]) == 0) {
                $aSDMean[$i] = 0;
            } else {
                $aSDMean[$i] = (floatval($aRatio[$i]) - floatval($aAverage[$i])) / floatval($aSTDEV[$i]);
            }
        }
        return $aSDMean;
    }

    /*
    * get spread
    * */
    public function spread($aData1, $aData2)
    {
        $aSpread = array();
        $count_aData1 = count($aData1);
        for ($i = 0; $i < $count_aData1; $i++) {
            if (isset($aData1[$i]) && !isset($aData2[$i])) {
                $aSpread[$i] = floatval($aData1[$i]);
            } elseif (!isset($aData1[$i]) && isset($aData2[$i])) {
                $aSpread[$i] = 0 - floatval($aData2[$i]);
            } elseif (isset($aData1[$i]) && isset($aData2[$i])) {
                $aSpread[$i] = floatval($aData1[$i]) - floatval($aData2[$i]);
            }
        }
        return $aSpread;
    }

    public function getArraySignal($aSDMean, $symbol1, $symbol2)
    {
        $aSignal = array();
        $flagNegative = 0;
        $flagPositive = 0;
        for ($i = (count($aSDMean) - 1); $i >= 0; $i--) {
            if (floatval($aSDMean[$i]) < -2.5 && $flagNegative == 0) {
                $aSignal[$i] = "buy " . $symbol1 . " sell " . $symbol2;
                $flagNegative = 1;
            } elseif (floatval($aSDMean[$i]) < -1.5 && $flagNegative == 1) {
                $aSignal[$i] = "hold";
            } elseif (floatval($aSDMean[$i]) > -1.5 && $flagNegative == 1) {
                $aSignal[$i] = "sell " . $symbol1 . " buy " . $symbol2;
                $flagNegative = 0;
            } elseif (floatval($aSDMean[$i]) > 2.5 && $flagPositive == 0) {
                $aSignal[$i] = "Short " . $symbol1 . "/ Long " . $symbol2;
                $flagPositive = 1;
            } elseif (floatval($aSDMean[$i]) > 1.5 && $flagPositive == 1) {
                $aSignal[$i] = "hold";
            } elseif (floatval($aSDMean[$i]) < 1.5 && $flagPositive == 1) {
                $aSignal[$i] = "buy " . $symbol1 . " sell " . $symbol2;
                $flagPositive = 0;
            } else {
                $aSignal[$i] = "";
            }
        }
        return $aSignal;
    }

    public function getDataBackTest($aData1, $aData2, $symbol1, $symbol2)
    {
        $aArrayClose1 = $this->getArrayClosePrice($aData1);
        $aArrayClose2 = $this->getArrayClosePrice($aData2);
        $aArrayDate = $this->getArrayDate($aData1);
        $aArrayRatio = $this->getArrayRatio($aArrayClose1, $aArrayClose2);
        $aArraySTDEV = $this->getArraySTDEV($aArrayRatio);
        $aArrayAverage = $this->getArrayAverage($aArrayRatio);
        $aArraySDMean = $this->sdMean($aArrayRatio, $aArraySTDEV, $aArrayAverage);
        $aArraySignal = $this->getArraySignal($aArraySDMean, $symbol1, $symbol2);

        $totalTrade = 0;
        $totalTradeDate = 0;
        $aTrade = array();
        $aTradeProfit = array();
        $maxTotalProfit = 0;
        $totalProfit = 0;
        
        $count_aArraySignal = count($aArraySignal);
        for ($i = 0; $i < $count_aArraySignal; $i++) {
            if ($aArraySignal[$i] != '' && $aArraySignal[$i] != 'hold') {
                $bIsATrade = false;
                $j = $i + 1;
                for (; $j < $count_aArraySignal; $j++) {
                    if ($aArraySignal[$j] == 'hold') {
                        continue;
                    } elseif ($aArraySignal[$j] == '') {
                        break;
                    } else {
                        $bIsATrade = true;
                        break;
                    }
                }
                if ($bIsATrade) {
                    if (strpos($aArraySignal[$j],'Short') || strpos($aArraySignal[$j],'Long')) {
                        array_push($aTrade, array('end' => $j, 'begin' => $i, 'isChange'=>1));
                    }else{
                        array_push($aTrade, array('end' => $i, 'begin' => $j, 'isChange'=>0));
                    }
                    $i = $j;
                }
            }
        }

        if (empty($aTrade)) {
            return null;
        } else {
            foreach ($aTrade as $item) {
                $aDataA = array('sellprice' => $aArrayClose1[$item['end']], 'buyprice' => $aArrayClose1[$item['begin']]);
                $aDataB = array('sellprice' => $aArrayClose2[$item['begin']], 'buyprice' => $aArrayClose2[$item['end']]);
                $aProfit = array();
                $countDate = abs($item['begin'] - $item['end']) + 1;
                $aProfit = $this->getBackTestNetProfit($aDataA, $aDataB, $countDate);
                if ($item['isChange'] ==1) {
                    $aProfit['beginDate'] = $aArrayDate[$item['end']];
                    $aProfit['endDate'] = $aArrayDate[$item['begin']];
                }else{
                    $aProfit['beginDate'] = $aArrayDate[$item['begin']];
                    $aProfit['endDate'] = $aArrayDate[$item['end']];
                }
                $totalProfit += $aProfit['netProfit'];
                if ($aProfit['netProfit'] > $maxTotalProfit) {
                    $maxTotalProfit = $aProfit['netProfit'];
                }
                array_push($aTradeProfit, $aProfit);
            }
        }

        $totalTrade = count($aTrade);
        foreach ($aTrade as $trade) {
            $totalTradeDate += abs($trade['begin'] - $trade['end']) + 1;
        }
        
        $avgData['avgDate'] = round($totalTradeDate / $totalTrade, 2);
        $avgData['avgProfitPerTrade'] = round($totalProfit / $totalTrade, 2);
        return array(
            'aProfit'        => $aTradeProfit,
            'totalProfit'    => $totalProfit,
            'avgData'        => $avgData,
            'maxTotalProfit' => $maxTotalProfit);
    }

    private function getBackTestNetProfit($aDataA, $aDataB, $countDate)
    {
        $profitA = ($aDataA['sellprice'] - $aDataA['buyprice']) * round((10000 / $aDataA['sellprice']), 2);
        $profitB = ($aDataB['sellprice'] - $aDataB['buyprice']) * round((10000 / $aDataB['buyprice']), 2);
        $totalProfit = $profitA + $profitB;
        $profitPercentage = $totalProfit / 20000 * 100;
        $profitPerTrade = $totalProfit / $countDate;

        return array(
            'netProfit' => round($totalProfit, 2),
            'profitPercentage' => abs(round($profitPercentage, 2)),
            'profitPerTrade' => abs(round($profitPerTrade, 2)),
            'noOfDayInTrade' => abs($countDate));
    }
}
